/**
 * Captain
 * © 2017 Captain copyright，All rights reserved.
 * http://www.sccaptain.com.cn
 * <p>
 * JAVA : 8
 * 文  件  名: DictUtils.java
 * 创  建  人: liujian (liu.jian@sccaptain.com.cn)
 * 创建时间: 2017/7/14 16:41
 * 版         本: 1.0.0
 * 备         注:
 * 修订历史:
 */

package com.material.c2c.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.validation.constraints.NotNull;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.StringUtils;

import com.cpt.framework.core.SystemRuntimeException;
import com.material.c2c.sysconfig.entity.XTPZ_SJZD;
import com.material.c2c.sysconfig.service.DataDictionaryService;

/**
 * 字典表工具类
 *
 * @since 1.0
 * @version 1.0
 * @author liujian (liu.jian@sccaptain.com.cn)
 */
public class DictUtils implements ApplicationContextAware {

  private static DataDictionaryService dataDictionaryService;

  public static final String PACKAGE_PREFIX = "com.cpt";

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    dataDictionaryService = applicationContext.getBean(DataDictionaryService.class);
  }

  /**
   * 根据dictCode和dictValue查出dictName，设值到指定对象的指定属性。
   * @param targetObject
   * @param targetProperty
   * @param dictCode
   * @param dictValue
   */
  public static void setNameByCode(@NotNull Object targetObject, @NotNull String targetProperty, @NotNull String dictCode, String dictValue){
    if (dictValue == null) return;
    XTPZ_SJZD sjzd = dataDictionaryService.findDictionaryItemValueEntity(dictCode, dictValue);
    if (sjzd == null) return;
    String mc = StringUtils.hasText(sjzd.getZdymc()) ? sjzd.getZdymc() : sjzd.getMc();
    try {
      Field field = targetObject.getClass().getDeclaredField(targetProperty);
      field.setAccessible(true);
      field.set(targetObject, mc);
    } catch (Exception e) {
      throw new SystemRuntimeException(e.getMessage(), e);
    }
  }

  /**
   * @see DictUtils#setNameByCode(Object, String, String, String)
   */
  public static void setNameByCode(@NotNull Object targetObject, @NotNull String targetProperty, @NotNull String dictCode, Integer dictValue){
    if (dictValue == null) return;
    DictUtils.setNameByCode(targetObject, targetProperty, dictCode, dictValue.toString());
  }

  /**
   * 设置有DictProperty或者EnumProperty注解的属性的字典名称。
   * 如果使用EnumProperty，指定枚举类型必须要有 name value 属性
   * 由于使用了递归，存在对象循环引用的情况，不要使用此方法
   * @param targetObject
   */
  public static void setDictName(Object targetObject){
    if (targetObject == null) return;
    List<Field> fieldsList = new ArrayList<>();
    Field[] fields =  targetObject.getClass().getDeclaredFields();
    fieldsList.addAll(Arrays.asList(fields));
    Class superClass = targetObject.getClass().getSuperclass();
    while (superClass.getPackage().getName().startsWith(PACKAGE_PREFIX)) {
      Field[] fieldArray = superClass.getDeclaredFields();
      fieldsList.addAll(Arrays.asList(fieldArray));
      superClass = superClass.getSuperclass();
    }
    for (Field field : fieldsList) {
      try {
        field.setAccessible(true);
        if (field.isAnnotationPresent(DictProperty.class)) {
          DictProperty dictProperty = field.getAnnotation(DictProperty.class);
          String dictCode = dictProperty.code();
          String valueProperty = dictProperty.valueProperty();

          Field valueField = targetObject.getClass().getDeclaredField(valueProperty);
          valueField.setAccessible(true);
          Object dictValue = valueField.get(targetObject);
          if (dictValue == null) continue;
          XTPZ_SJZD sjzd = dataDictionaryService.findDictionaryItemValueEntity(dictCode, dictValue.toString());
          if (sjzd == null) continue;
          String mc = StringUtils.hasText(sjzd.getZdymc()) ? sjzd.getZdymc() : sjzd.getMc();
          field.setAccessible(true);
          field.set(targetObject, mc);
        } else if (field.isAnnotationPresent(EnumProperty.class)) {
          EnumProperty dictProperty = field.getAnnotation(EnumProperty.class);
          Class<?> enumType = dictProperty.type();
          String valueProperty = dictProperty.valueProperty();

          Field valueField = targetObject.getClass().getDeclaredField(valueProperty);
          valueField.setAccessible(true);
          Object dictValue = valueField.get(targetObject);
          if (dictValue == null) continue;
          Map<String, String> enumMap = getEnumMap(enumType);
          String enumName = enumMap.get(String.valueOf(dictValue));
          if (enumName == null) continue;
          field.setAccessible(true);
          field.set(targetObject, enumName);
        } else {
          Object valueObj = field.get(targetObject);
          if (valueObj != null && field.getDeclaringClass().getPackage().getName().startsWith(PACKAGE_PREFIX)) {
            DictUtils.setDictName(valueObj);
          }
          if (valueObj != null && valueObj instanceof Collection) {
            DictUtils.setDictName((Collection)valueObj);
          }
        }
      } catch (Exception e) {
        throw new SystemRuntimeException(e.getMessage(), e);
      }
    }
  }

  /**
   * @see DictUtils#setDictName(Object)
   * @param targetObject
   */
  public static void setDictName(Collection targetObject){
    if (targetObject == null) return;
    Iterator<Object> iterator = targetObject.iterator();
    while (iterator.hasNext()) {
      Object obj = iterator.next();
      if (!obj.getClass().getPackage().getName().startsWith(PACKAGE_PREFIX)) {
        break;
      }
      DictUtils.setDictName(obj);
    }
  }

  /**
   * 读取枚举常量的 name value，转成map
   * @param enumType
   * @return
   */
  private static Map<String, String> getEnumMap(Class<?> enumType) {
    if (!enumType.isEnum()) {
      throw new SystemRuntimeException(enumType.getName() + "不是枚举类型");
    }
    String nameMethodName = "getName", valueMethodName = "getValue";
    Enum[] enumConstants = (Enum[]) enumType.getEnumConstants();
    Map<String, String> result = new HashMap<>();
    for (Enum obj : enumConstants) {
      Class declaringClass = obj.getDeclaringClass();
      try {
        Method nameMethod = declaringClass.getMethod(nameMethodName);
        Object name = nameMethod.invoke(obj);
        Method valueMethod = declaringClass.getMethod(valueMethodName);
        Object val = valueMethod.invoke(obj);
        result.put(String.valueOf(val), String.valueOf(name));
      } catch (Exception e) {
        throw new SystemRuntimeException(e.getMessage(), e);
      }
    }
    return result;
  }

}
